1 module hip.game2d.tileworld;
2 public import hip.api.data.tilemap;
3 public import hip.component.physics;
4 public import hip.math.collision;
5 import hip.util.algorithm;
6 
7 
8 
9 class TileWorld
10 {
11     IHipTilemap map;
12     ///Affects position
13     float constantGravity = 0;
14     ///Affects velocity
15     float gravity = 0;
16 
17     BodyRectComponent[] dynamicBodies;
18     HipTileLayer[] collidibleLayers;
19 
20     this(IHipTilemap map, float gravity = 0, float constantGravity = 0)
21     {
22         this.map = map;
23         this.gravity = gravity;
24         this.constantGravity = constantGravity;
25     }
26 
27     private Rect[128] rectCache;
28     Rect[] getRectsOverlapping(HipTileLayer layer, in Rect input) @nogc
29     {
30         import hip.math.utils;
31         if(!layer.isInLayerBoundaries(
32             cast(int)input.x, cast(int)input.y, cast(int)input.w, cast(int)input.h
33         ))
34             return rectCache[0..0];
35         
36         uint tileW = map.tileWidth, tileH = map.tileHeight;
37         float x = input.x - layer.x;
38         float y = input.y - layer.y;
39         
40 
41 
42         
43 
44         float inputTilesWidth = input.w / tileW;
45         float inputTilesHeight = input.h / tileH;
46 
47         float i = x/tileW;
48         float j = y/tileH;
49         float i2 = ceil(min(i + inputTilesWidth, layer.width));
50         float j2 = ceil(min(j + inputTilesHeight, layer.height - 1));
51 
52 
53         float plusI = min(inputTilesWidth, 1);
54         float plusJ = min(inputTilesHeight, 1);
55 
56         int lastI = -1;
57         int lastJ = -1;
58 
59         int rects = 0;
60         for(; j < j2; j+= plusJ)
61         for(float _i = i; _i < i2; _i+= plusI)
62         {
63             if(_i < 0 || j < 0)
64                 continue;
65             int ui = cast(int)_i;
66             int uj = cast(int)j;
67 
68             if(ui == lastI && uj == lastJ)
69                 continue;
70             lastI = ui;
71             lastJ = uj;
72 
73             ushort tID = layer.checkedGetTile(ui, uj);
74             if(tID != 0)
75             {
76                 rectCache[rects++] = Rect(layer.x + ui*tileW, layer.y + uj*tileH, tileW, tileH);
77             }
78         }
79         return rectCache[0..rects];
80     }
81 
82     void addDynamic(IComponentizable component)
83     {
84         auto comp = component.getComponent!BodyRectComponent;
85         assert(comp !is null, "No BodyRectComponent found. Ensure that you have done mixin IncludeComponents!(BodyRectComponent) and you had called initializeComponents() somewhere");
86         assert(comp.size.w != 0, "Can't have 0 width component");
87         assert(comp.size.h != 0, "Can't have 0 height component");
88         dynamicBodies~= comp;
89     }
90 
91     void addCollidibleLayer(HipTileLayer layer)
92     {
93         collidibleLayers~= layer;
94     }
95 
96     void update(float dt) @nogc
97     {
98         import hip.util.algorithm:quicksort;
99         struct DynamicRectCollision
100         {
101             Vector2 normal;
102             float time;
103         }
104         __gshared DynamicRectCollision[128] colCache;
105         foreach(dynBody; dynamicBodies)
106         {
107             dynBody.velocity.y += gravity;
108             Rect bodyRec = dynBody.rect;
109             int z = 0;
110 
111             foreach(HipTileLayer l; collidibleLayers)
112             for(int j = 0; j < l.height; j++)
113             for(int i = 0; i < l.width; i++)
114             {
115                 if(l.getTile(i, j) != 0)
116                 {
117                     DynamicRectCollision col = void;
118                     if(isDynamicRectOverlappingRect(bodyRec, dynBody.velocity, Rect(l.x + i*l.tileWidth, l.y+j*l.tileHeight, l.tileWidth, l.tileHeight), dt, col.normal, col.time))
119                         colCache[z++] = col;
120                 }
121             }
122             if(z > 0)
123             foreach(col; quicksort(colCache[0..z], ((DynamicRectCollision a, DynamicRectCollision b) => a.time < b.time)))
124                 resolveDynamicRectOverlappingRect(col.normal, dynBody.velocity, col.time);
125             dynBody.position+= dynBody.velocity* dt;
126         }
127     }
128 }